package org.andengine.shmup;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;

import org.anddev.andengine.engine.handler.timer.ITimerCallback;
import org.anddev.andengine.engine.handler.timer.TimerHandler;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.andengine.bulletml.simple.Action;
import org.andengine.bulletml.simple.BossData;
import org.andengine.bulletml.simple.PointXML;

public class FinalBoss extends Boss {

	/*
	 * Make sure the name input matches with what's being extracted from the
	 * poolMap (see below).
	 */
	public HashMap<String, EnemyPool> poolMap = new HashMap<String, EnemyPool>();

	/*
	 * to Isaiah:
	 * 
	 * I changed my mind abotu the options. They should be setVisible(false) instead.
	 * I'm sure the constant grunt spawning will have an adverse affect on memory.
	 */
	private static final int MAX_OPTIONS = 4;
	private static final int OPTIONS_MAX_HP = 600;
	private float laserWidth;
	private boolean optionBeams =false;
	public boolean optionsInit = false;
	private float radius, basePX = this.getCenterX() + radius, basePY = this.getCenterY();
	public Enemy[] options = new Enemy[MAX_OPTIONS];
	
	//Obvious name is obvious.
	public ArrayList<Enemy> notGrunts = new ArrayList<Enemy>();

	public FinalBoss(float pX, float pY, TiledTextureRegion pTextureRegion,
			BossData bd, TiledTextureRegion beam) {
		super(pX, pY, pTextureRegion, bd, beam);
		// TODO Auto-generated constructor stub
		laserWidth = beam.getWidth();
		radius = 2*this.getWidth()/3;
		optionsInit = false;
	}

	@Override
	protected int actHelp(Action a, String source) {
		if (a.getLabel().contains("spawnGrunt")) {
			for (int i = 0; i<MAX_OPTIONS; i++) {
				if(this.powerLevel < 1) break;
				if(this.powerLevel < 2 && i >= 1) break;
				if(this.powerLevel < 3 && i >= 2) break;
				if(!options[i].isVisible()){
					options[i].setVisible(true);
					options[i].firing = true;
					options[i].maxShots = 3;
					options[i].hp = OPTIONS_MAX_HP;
					options[i].setPosition(this.getCenterX() + (radius + (i*15))*(float)Math.pow(-1, i), this.getCenterY()-this.getHeight()*(i/2)/3);
				}		
			}
			return 1;
		} else if(a.getLabel().contains("levelUp") && powerLevel <3) {
			this.powerLevel++;
			return 1;
		} else if(a.getLabel().contains("levelDown") && powerLevel >1) {
			this.powerLevel--;
			return 1;
		} else if(a.getLabel().contains("levelReset")) {
			this.powerLevel = 0;
			return 1;
		} else {
			for(int i = 0; i < MAX_OPTIONS; i++){
				if(options[i].isVisible()){
					if(a.getPriority().equals("HIGH")){
						options[i].firing = false;
						options[i].def = 0.9f + (1/this.powerLevel)*.05f;
					}
					if(a.equals("optionBeam")){
						options[i].firing = !a.active;
						optionBeams = a.active;
					}
				}
			}
			return super.actHelp(a, source);
		}
	}

	@Override
	public void move(float delta, Action a) {
		super.move(delta, a);
		for (Enemy e : options) {
			if (e != null) {
				e.setPosition(e.getX() + this.v.x, e.getY() + this.v.y);
			}
		}
	}
	
	@Override
	protected void onManagedUpdate(float pSecondsElapsed){
		basePX = this.getCenterX() + radius;
		basePY = this.getCenterY() + radius;
		this.checkOptions(pSecondsElapsed);
		super.onManagedUpdate(pSecondsElapsed);
	}
	
	private void spawnEnemy(EnemyPool ep, String label) {
		Enemy proxy = ep.obtainPoolItem();
		if(label.contains("Blader")){
			proxy.setVelocity(0, 60);
		}
		proxy.setPosition(this.getCenterX(), this.getCenterY() + this.getHeight()/2);
		this.notGrunts.add(proxy);
		if (!proxy.hasParent()) {
			this.getParent().attachChild(proxy);
		}
	}
	private void checkOptions(float delta){
		int n1 = 0, n2 = 0, bladerBound = 1 + (2*powerLevel);
		for(int i = 0; i < MAX_OPTIONS; i++){
			if(options[i].isVisible()){
				n1++;
				options[i].def = 1 + (1/this.powerLevel)*.05f;
				switch(powerLevel){
				case 1:{
					
					options[i].maxShots = 3;
					if(i>1){
						options[i].hp = options[i].hp%300;
						options[i].def = 1.15f;
						options[i].maxShots = 2;
					}
					if(options[i].getX() != basePX + (i*15)){
						options[i].setVelocity((basePX + i*laserWidth - options[i].getX())/.5f, options[i].v.y);
					}
					else{
						options[i].setVelocity(0, options[i].v.y);
					}
					if(options[i].getY() != basePY -this.getHeight()*(i/2)/3){
						options[i].setVelocity(options[i].v.x,(basePY -this.getHeight()*(i/2)/3 - options[i].getY())/.5f);
					}
					else{
						options[i].setVelocity(options[i].v.x,0);
					}
					break;
				}
				case 2:{
					options[i].maxShots = 4 ;
					if((int)(this.timeAlive)%9==0 && options[i].hp < OPTIONS_MAX_HP*.3) options[i].hp += 3;
					
					if(i>2){
						options[i].hp = options[i].hp%600;
						options[i].def =1.05f;
						options[i].maxShots = 3;
					}
					
					if(options[i].getX() != basePX + (i*15)){
						options[i].setVelocity((basePX + i*laserWidth - options[i].getX())/.5f, options[i].v.y);
					}
					else{
						options[i].setVelocity(2*(radius + Math.round(i/2)*15)*(float)Math.pow(-1, i+1)*-(float)Math.sin(Math.toRadians(this.timeAlive)),options[i].v.y);
					}
					
					if(options[i].getY() != basePY -this.getHeight()*(i/2)/3){
						options[i].setVelocity(options[i].v.x,(basePY -this.getHeight()*(i/2)/3 - options[i].getY())/.5f);
					}
					else{
						options[i].setVelocity(options[i].v.x,0);
					}
					
					break;
				}
				case 3:{
					for(AnimatedSprite l: this.lasers){
						l.setPosition(options[i].getCenterX(), options[i].getCenterY() + options[i].getHeight()/2);
					}
					if(optionBeams){
						
						if(options[i].getX() != basePX + (i*15)){
							options[i].setVelocity((basePX + i*laserWidth - options[i].getX())/.5f, options[i].v.y);
						}
						else{
							options[i].setVelocity(0, options[i].v.y);
						}
						
						if(options[i].getY() != basePY -this.getHeight()*(i/2)/3){
							options[i].setVelocity(options[i].v.x,(basePY -this.getHeight()*(i/2)/3 - options[i].getY())/.5f);
						}
						else{
							options[i].setVelocity(options[i].v.x,0);
						}
					}
					else{
						options[i].setPosition((float)Math.pow(-1, (int)(i/2))*basePX+(i*15)*(float)(Math.cos(this.timeAlive*(1+(int)(i/2)))),
							(float)Math.pow(-1, (int)(i/2))*basePY+(i*15)*(float)(Math.sin(this.timeAlive*(1+(int)(i/2)))));
					}
					options[i].maxShots = 5 ;
					if((int)(this.timeAlive)%9==0 && options[i].hp < OPTIONS_MAX_HP*.3) options[i].hp += 6;
					
					break;
				}
				}		
			}
			else{
				options[i].maxShots = 0;
				options[i].firing = false;
				options[i].setVisible(false);
				options[i].setVelocity(0, 0);
			}
		}
		if(bladerBound > notGrunts.size()) bladerBound = notGrunts.size();
		for(int i = 0; i < bladerBound; i++){
			n2++;
		}
		regen = 9*(n1/MAX_OPTIONS + (powerLevel/3));
		dmgMod = data.getDefMod() - .375f*((1/MAX_OPTIONS)*(n1) + ((n2)/(1 + (2*powerLevel))));
	}
	
	public void initOptions(){
		for(int i = 0; i <MAX_OPTIONS;i++){
			if(options[i] == null){
				options[i] = poolMap.get("grunt").obtainPoolItem();
			}
			options[i].firing = false;
			options[i].setVisible(false);
			options[i].setBulletPool(this.bulletPool);
			options[i].setBulletArray(this.bullets);
			options[i].setVelocity(0, 0);
			if (!options[i].hasParent()) {
				this.getParent().attachChild(options[i]);
			}
		}
	}
	
	@Override
	protected void timerInit(Collection<Action> c, TiledTextureRegion beam){
		for(final Action a: c){
			if (a.getLabel().contains("spawnBladers")) {
				timers.put(a.getLabel(), new TimerHandler(a.getFiringGap(),
						true, new ITimerCallback() {
							@Override
							public void onTimePassed(
									final TimerHandler pTimerHandler) {
								spawnEnemy(poolMap.get("blader"), a.getLabel());
								pTimerHandler.reset();
							}
							
						}));
			}
			else if (a.getLabel().contains("optionBeam")){
				for(int i = 0; i<MAX_OPTIONS;i++){
					this.lasers.add(new AnimatedSprite(0, 0, beam));
				}
			}
		}
		super.timerInit(c, beam);
	}
}
